#include <GameEngine/Core/Color/color_rgb.h>

#include <GameEngine/Core/Color/color.h>

#include <QtGui/QColor>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=========================================================================================

//! Default constructor
ColorRGB::ColorRGB() : r_(0.0f), g_(0.0f), b_(0.0f) {}

//! Constructor
/*!
 @param r The red component of the color
 @param g The green component of the color
 @param b The blue component of the color
 */
ColorRGB::ColorRGB(float r, float g, float b) : r_(r), g_(g), b_(b) {}

//! Constructor
/*!
 @param rhs The color to copy, provided in the Qt format
 */
ColorRGB::ColorRGB(const QColor& rhs) :
	r_((float)rhs.red()/255.0f), g_((float)rhs.green()/255.0f), b_((float)rhs.blue()/255.0f) {}

//! Copy constructor
/*!
 @param rhs The color to copy
 */
ColorRGB::ColorRGB(const ColorRGB& rhs) : r_(rhs.r_), g_(rhs.g_), b_(rhs.b_) {}

//! Assignment operator
/*!
 @param rhs The color to copy
 @return A reference to this RGB color
 */
ColorRGB& ColorRGB::operator=(const ColorRGB& rhs) {
	if ( &rhs != this ) {
		r_ = rhs.r_;
		g_ = rhs.g_;
		b_ = rhs.b_;
	}
	return *this;
}

//! Destructor
ColorRGB::~ColorRGB() {}

//! Accessor to the red component (read only)
/*!
 @return The red component
 */
float ColorRGB::r() const {
	return r_;
}

//! Accessor to the red component (read-write)
/*!
 @return A reference to the red component
 */
float& ColorRGB::r() {
	return r_;
}

//! Accessor to the green component (read only)
/*!
 @return The green component
 */
float ColorRGB::g() const {
	return g_;
}

//! Accessor to the green component (read-write)
/*!
 @return A reference to the green component
 */
float& ColorRGB::g() {
	return g_;
}

//! Accessor to the blue component (read only)
/*!
 @return The blue component
 */
float ColorRGB::b() const {
	return b_;
}

//! Accessor to the blue component (read-write)
/*!
 @return A reference to the blue component
 */
float& ColorRGB::b() {
	return b_;
}

//! Accessor to the alpha component (read only)
/*!
 @return Always return 1.
 */
float ColorRGB::a() const {
	return 1.0f;
}

//! Compute the hue component of the color (read only)
/*!
 *  Hue is theoretically undefined for levels of gray; by convention, a hue of 0
 *  is returned for such colors. Please note that opposite assumption is incorrect:
 *  one can not assume color is a level of gray just because this function returns 0.
 *
 *  The red, green and blue components of the color should all be between 0 and 1
 *  to get this function working. This assumption is NOT checked by the function,
 *  however: client code is responbile for not messing up with the color.
 *
 @return The hue component, provided in range [0,1]
 */
float ColorRGB::h() const {
	// Get lowest component
	float m = ( ( r_ < g_ ) ? r_ : g_ );
	m = ( ( m < b_ ) ? m : b_ );

	// Get difference with the highest component
	float value = v();
	float delta = value - m;

	// If gray, leave hue at zero
	if ( delta <= 0.0f ) return 0.0f;

	// Find the larget color component and return hue accordingly
	if ( ( r_ >= g_ ) && ( r_ >= b_ ) ) return ( g_ - b_ ) / ( 6.0f * delta );
	if ( g_ >= b_ ) return ( 1.0f/3.0f ) + ( b_ - r_ ) / ( 6.0f * delta );
	return ( 2.0f/3.0f ) + ( r_ - g_ ) / ( 6.0f * delta );
}

//! Compute the saturation component of the color (read only)
/*!
 *  The red, green and blue components of the color should all be between 0 and 1
 *  to get this function working. This assumption is NOT checked by the function,
 *  however: client code is responbile for not messing up with the color.
 *
 @return The saturation component, provided in range [0,1] (0 means color is a gray level)
 */
float ColorRGB::s() const {
	// Get lowest component
	float m = ( ( r_ < g_ ) ? r_ : g_ );
	m = ( ( m < b_ ) ? m : b_ );

	// Get difference with the highest component
	float value = v();
	float delta = value - m;

	// If gray, leave saturation at zero
	if ( delta <= 0.0f ) return 0.0f;

	// Return saturation
	return delta / value;
}

//! Compute the value component of the color (read only)
/*!
 *  The red, green and blue components of the color should all be between 0 and 1
 *  to get this function working. This assumption is NOT checked by the function,
 *  however: client code is responbile for not messing up with the color.
 *
 @return The value component, provided in range [0,1] (0 means color is black)
 */
float ColorRGB::v() const {
	float m = ( ( r_ > g_ ) ? r_ : g_ );
	return ( ( m > b_ ) ? m : b_ );
}

//! Test if color is achromatic (i.e., if it is a level of gray)
/*!
 @param tolerance Allows to capture "almost gray" colors
 @return True if color is achromatic, false otherwise
 */
bool ColorRGB::achromatic(float tolerance) const {
	if ( abs(r_-g_) > tolerance ) return false;
	if ( abs(g_-b_) > tolerance ) return false;
	if ( abs(b_-r_) > tolerance ) return false;
	return true;
}

//=========================================================================================

//! Default constructor
ColorRGBA::ColorRGBA() : r_(0.0f), g_(0.0f), b_(0.0f), a_(0.0f) {}

//! Constructor
/*!
 @param r The red component of the color
 @param g The green component of the color
 @param b The blue component of the color
 @param a The alpha component of the color
 */
ColorRGBA::ColorRGBA(float r, float g, float b, float a) : r_(r), g_(g), b_(b), a_(a) {}

//! Constructor
/*!
 @param rhs The color to copy, provided in the Qt format
 */
ColorRGBA::ColorRGBA(const QColor& rhs) :
	r_((float)rhs.red()/255.0f), g_((float)rhs.green()/255.0f), b_((float)rhs.blue()/255.0f), a_((float)rhs.alpha()/255.0f) {}

//! Copy constructor
/*!
 @param rhs The color to copy
 */
ColorRGBA::ColorRGBA(const ColorRGBA& rhs) : r_(rhs.r_), g_(rhs.g_), b_(rhs.b_), a_(rhs.a_) {}

//! Assignment operator
/*!
 @param rhs The color to copy
 @return A reference to this RGBA color
 */
ColorRGBA& ColorRGBA::operator=(const ColorRGBA& rhs) {
	if ( &rhs != this ) {
		r_ = rhs.r_;
		g_ = rhs.g_;
		b_ = rhs.b_;
		a_ = rhs.a_;
	}
	return *this;
}

//! Destructor
ColorRGBA::~ColorRGBA() {}

//! Accessor to the red component (read only)
/*!
 @return The red component
 */
float ColorRGBA::r() const {
	return r_;
}

//! Accessor to the red component (read-write)
/*!
 @return A reference to the red component
 */
float& ColorRGBA::r() {
	return r_;
}

//! Accessor to the green component (read only)
/*!
 @return The green component
 */
float ColorRGBA::g() const {
	return g_;
}

//! Accessor to the green component (read-write)
/*!
 @return A reference to the green component
 */
float& ColorRGBA::g() {
	return g_;
}

//! Accessor to the blue component (read only)
/*!
 @return The blue component
 */
float ColorRGBA::b() const {
	return b_;
}

//! Accessor to the blue component (read-write)
/*!
 @return A reference to the blue component
 */
float& ColorRGBA::b() {
	return b_;
}

//! Accessor to the alpha component (read only)
/*!
 @return The alpha component
 */
float ColorRGBA::a() const {
	return a_;
}

//! Accessor to the alpha component (read-write)
/*!
 @return A reference to the alpha component
 */
float& ColorRGBA::a() {
	return a_;
}

//! Compute the hue component of the color (read only)
/*!
 *  Hue is theoretically undefined for levels of gray; by convention, a hue of 0
 *  is returned for such colors. Please note that opposite assumption is incorrect:
 *  one can not assume color is a level of gray just because this function returns 0.
 *
 *  The red, green and blue components of the color should all be between 0 and 1
 *  to get this function working. This assumption is NOT checked by the function,
 *  however: client code is responbile for not messing up with the color.
 *
 @return The hue component, provided in range [0,1]
 */
float ColorRGBA::h() const {
	// Get lowest component
	float m = ( ( r_ < g_ ) ? r_ : g_ );
	m = ( ( m < b_ ) ? m : b_ );

	// Get difference with the highest component
	float value = v();
	float delta = value - m;

	// If gray, leave hue at zero
	if ( delta <= 0.0f ) return 0.0f;

	// Find the larget color component and return hue accordingly
	if ( ( r_ >= g_ ) && ( r_ >= b_ ) ) return ( g_ - b_ ) / ( 6.0f * delta );
	if ( g_ >= b_ ) return ( 1.0f/3.0f ) + ( b_ - r_ ) / ( 6.0f * delta );
	return ( 2.0f/3.0f ) + ( r_ - g_ ) / ( 6.0f * delta );
}

//! Compute the saturation component of the color (read only)
/*!
 *  The red, green and blue components of the color should all be between 0 and 1
 *  to get this function working. This assumption is NOT checked by the function,
 *  however: client code is responbile for not messing up with the color.
 *
 @return The saturation component, provided in range [0,1] (0 means color is a gray level)
 */
float ColorRGBA::s() const {
	// Get lowest component
	float m = ( ( r_ < g_ ) ? r_ : g_ );
	m = ( ( m < b_ ) ? m : b_ );

	// Get difference with the highest component
	float value = v();
	float delta = value - m;

	// If gray, leave saturation at zero
	if ( delta <= 0.0f ) return 0.0f;

	// Return saturation
	return delta / value;
}

//! Compute the value component of the color (read only)
/*!
 *  The red, green and blue components of the color should all be between 0 and 1
 *  to get this function working. This assumption is NOT checked by the function,
 *  however: client code is responbile for not messing up with the color.
 *
 @return The value component, provided in range [0,1] (0 means color is black)
 */
float ColorRGBA::v() const {
	float m = ( ( r_ > g_ ) ? r_ : g_ );
	return ( ( m > b_ ) ? m : b_ );
}

//! Test if color is achromatic (i.e., if it is a level of gray)
/*!
 @param tolerance Allows to capture "almost gray" colors
 @return True if color is achromatic, false otherwise
 */
bool ColorRGBA::achromatic(float tolerance) const {
	if ( abs(r_-g_) > tolerance ) return false;
	if ( abs(g_-b_) > tolerance ) return false;
	if ( abs(b_-r_) > tolerance ) return false;
	return true;
}

//=========================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
